package com.example.springboothomework.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.springboothomework.common.ImportError;
import com.example.springboothomework.common.ImportResult;
import com.example.springboothomework.common.PageResponse;
import com.example.springboothomework.config.EmployeeExcelListener;
import com.example.springboothomework.config.LocalDateConverter;
import com.example.springboothomework.dao.CompanyDao;
import com.example.springboothomework.dao.EmployeeDao;
import com.example.springboothomework.entity.Employee;
import com.example.springboothomework.entity.QEmployee;
import com.example.springboothomework.entity.dto.EmployeeDTO;
import com.example.springboothomework.entity.excel.EmployeeExcelExport;
import com.example.springboothomework.entity.excel.EmployeeExcelImport;
import com.example.springboothomework.entity.excel.EmployeeExcelImportWithError;
import com.example.springboothomework.entity.query.EmployeeExcelExportQuery;
import com.example.springboothomework.exception.BusinessException;
import com.example.springboothomework.exception.ResultCodeEnum;
import com.example.springboothomework.mapper.EmployeeMapper;
import com.example.springboothomework.service.IEmployeeService;
import com.querydsl.core.BooleanBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Validator;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 员工service接口实现类
 *
 * @author chenjianhao
 * @date 2025/6/6
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EmployeeServiceImpl implements IEmployeeService {
    //用于进行导入员工数据的校验启动
    private final Validator validator;
    private final EmployeeDao employeeDao;
    private final CompanyDao companyDao;
    private final EmployeeMapper employeeMapper;

    /**
     * 添加员工
     *
     * @param employeeDTO
     * @return
     */
    @Override
    public Long save(EmployeeDTO employeeDTO) {
        //清空名字两边空格
        employeeDTO.setName(employeeDTO.getName().trim());
        //公司是否存在
        if (!companyDao.existsById(employeeDTO.getCompanyId())) {
            throw new BusinessException(ResultCodeEnum.COMPANY_NOT_EXITS);
        }
        //添加员工
        Employee employee = employeeMapper.toEntity(employeeDTO);
        return employeeDao.save(employee).getId();
    }

    /**
     * 删除员工
     *
     * @param id
     * @return
     */
    @Override
    public void delete(Long id) {
        //员工是否存在
        if (!employeeDao.existsById(id)) {
            throw new BusinessException(ResultCodeEnum.EMPLOYEE_NOT_EXIST);
        }
        //删除员工
        employeeDao.deleteById(id);
    }

    /**
     * 修改员工
     *
     * @param employeeDTO
     * @return
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {
        //员工是否存在，存在则查出
        Employee employee = employeeDao.findById(employeeDTO.getId())
                .orElseThrow(() -> new BusinessException(ResultCodeEnum.EMPLOYEE_NOT_EXIST));

        //公司是否存在
        if (!companyDao.existsById(employeeDTO.getCompanyId())) {
            throw new BusinessException(ResultCodeEnum.COMPANY_NOT_EXITS);
        }

        //更新员工
        employeeMapper.updateEntity(employeeDTO, employee);
        employeeDao.save(employee);
    }

    /**
     * 根据id查询员工
     *
     * @param id
     * @return
     */
    @Override
    public Employee info(Long id) {
        return employeeDao.findById(id)
                .orElseThrow(() -> new BusinessException(ResultCodeEnum.EMPLOYEE_NOT_EXIST));
    }

    /**
     * 分页查询员工 Querydsl
     *
     * @param pageNum
     * @param pageSize
     * @param searchKeyword
     * @param companyId
     * @return
     */
    @Override
    public PageResponse<Employee> queryPage(Integer pageNum, Integer pageSize, String searchKeyword, Long companyId) {
        // 分页参数初始化
        int pNum = (pageNum == null || pageNum < 1) ? 1 : pageNum;
        int pSize = (pageSize == null || pageSize < 1) ? 10 : pageSize;

        // 构建分页和排序
        Pageable pageable = PageRequest.of(pNum - 1, pSize, Sort.by(Sort.Direction.DESC, "employmentDate"));

        // 使用Querydsl构建查询条件
        QEmployee qEmployee = QEmployee.employee;
        BooleanBuilder predicate = new BooleanBuilder();

        //名字模糊查询
        if (searchKeyword != null && !searchKeyword.trim().isEmpty()) {
            String keyword = "%" + searchKeyword.trim() + "%";
            predicate.and(qEmployee.name.like(keyword));
        }
        // 公司ID筛选
        if (companyId != null && companyId > 0) {
            predicate.and(qEmployee.companyId.eq(companyId));
        }

        // 执行分页查询
        Page<Employee> page = employeeDao.findAll(predicate, pageable);

        return new PageResponse<>(
                pNum,
                pSize,
                page.getTotalElements(),
                page.getContent()
        );
    }


    /**
     * 导出员工
     *
     * @param employeeExcelExportQuery
     * @param response
     */
    @Override
    public void exportEmployees(EmployeeExcelExportQuery employeeExcelExportQuery,HttpServletResponse response) {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        /**
         * 解决中文乱码问题
         * URLEncoder.encode()会把空格编码成+，但在HTTP头中，建议用%20表示空格。
         * 因此，需要将+替换成%20
         */
        String fileName = "员工列表.xlsx";
        String encodedFileName;
        try {
            encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException(ResultCodeEnum.EXPORT_ERROR);
        }
        /**
         * Content-disposition 控制浏览器如何处理返回的文件
         * attachment 表示以附件形式下载
         * filename= 支持简单的ASCII字符，但对中文和特殊字符支持不好，可能会乱码。
         * filename*=（UTF-8编码方式）确保支持国际字符，得到最佳兼容。
         * 为什么所以utf-8后有''？
         * 使用filename*时，格式是filename*=编码''实际的文件名，
         */
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName);

        //分片参数
        int shardSize = 1000;
        Long lastId = 0L;
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(response.getOutputStream(), EmployeeExcelExport.class)
                    .registerConverter(new LocalDateConverter())
                    .build();
            WriteSheet writeSheet = EasyExcel.writerSheet("员工信息").build();

            boolean hasMore = true;
            while (hasMore) {
                List<EmployeeExcelExport> excelList = employeeDao.findByIdThanOrderByIdAscAndFilters(lastId,
                        employeeExcelExportQuery.getSearchKeyword(),
                        employeeExcelExportQuery.getCompanyId(),
                        PageRequest.of(0, shardSize));
                if (CollectionUtils.isEmpty(excelList)) {
                    excelWriter.write(Collections.emptyList(), writeSheet); // 处理空数据
                    break;
                }
                // 写入数据
                excelWriter.write(excelList, writeSheet);

                hasMore = (excelList.size() == shardSize);
                lastId = excelList.get(excelList.size() - 1).getId();
            }
        } catch (Exception e) {
            log.error("导出失败", e);
            throw new BusinessException(ResultCodeEnum.EXPORT_ERROR);
        } finally {
            if (excelWriter != null) {
                // 确保finish在finally中调用
                excelWriter.finish();
            }
            try {
                // 强制刷新流
                response.getOutputStream().flush();
            } catch (IOException e) {
                log.error("刷新输出流失败", e);
            }
        }
    }


    /**
     * 导入员工
     *
     * @param file
     * @return
     */
    @Override
    public ImportResult importEmployees(MultipartFile file) {
        try {
            // 创建监听器实例
            EmployeeExcelListener listener = new EmployeeExcelListener(
                    validator,
                    employeeDao,
                    employeeMapper,
                    companyDao
            );

            // 使用EasyExcel读取数据
            EasyExcel.read(file.getInputStream(), EmployeeExcelImport.class, listener)
                    .sheet()
                    .doRead();

            // 返回导入结果
            return new ImportResult(
                    listener.getTotal(),
                    listener.getSuccess(),
                    listener.getFail(),
                    listener.getErrors()
            );
        } catch (Exception e) {
            log.error("员工导入失败", e);
            throw new BusinessException(ResultCodeEnum.IMPORT_ERROR);
        }
    }

    /**
     * 导入员工有失败案例时，导出错误数据
     * @param errors
     * @param response
     */
    public void exportErrorData(List<ImportError> errors, HttpServletResponse response) {
        try {
            //导出数据
            List<EmployeeExcelImportWithError> errorDataList = new ArrayList<>();
            for (ImportError error : errors) {
                EmployeeExcelImportWithError employeeExcelImportWithError = employeeMapper.importToImportWithError(error.getData(), error.getMessage());
                errorDataList.add(employeeExcelImportWithError);
            }

            //设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("员工导入错误数据", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''"+ fileName + ".xlsx");

            // EasyExcel导出
            EasyExcel.write(response.getOutputStream(), EmployeeExcelImportWithError.class)
                    .registerConverter(new LocalDateConverter())
                    .sheet("导入错误数据")
                    .doWrite(errorDataList);
        } catch (IOException e) {
            log.error("导出错误数据失败", e);
            throw new BusinessException(ResultCodeEnum.EXPORT_ERROR);
        }
    }
}